﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace CrazyDrawShape
{
    /// <summary>
    /// DrawPolygonControl.xaml 的交互逻辑
    /// </summary>
    public partial class DrawPolygonControl : UserControl
    {
        private enum DrawType
        {
            Draw,
            Edit
        }
        private enum EditType
        {
            Point,
            Internal,
            None
        }
        private class PolygonPoint
        {
            public Point Point { set; get; }
            public Path Circle { set; get; }
        }

        Path polygon = new Path { Stroke = Brushes.Red, StrokeThickness = 2, Fill = Brushes.AliceBlue, Opacity = 0.5, ClipToBounds = true };
        List<PolygonPoint> points = new List<PolygonPoint>();
        bool isRun = false;
        Point downPoint;
        DrawType drawType = DrawType.Draw;
        EditType editType = EditType.None;
        int pointIndex = -1;
        int selectIndex = -1;

        private void DrawCircle()
        {
            foreach (PolygonPoint point in this.points)
            {
                Path circle = new Path { Stroke = Brushes.Red, StrokeThickness = 1, Fill = Brushes.Red, ClipToBounds = true };
                circle.Data = new EllipseGeometry { RadiusX = 3, RadiusY = 3, Center = point.Point };
                point.Circle = circle;
                circle.MouseUp += GridMouseUp;
                mainGrid.Children.Add(circle);
            }
        }

        private void DrawPolygon()
        {
            int length = this.points.Count;
            if (length != 0)
            {
                string pathData = null;
                for (int i = 0; i < length; i++)
                {
                    Point point = this.points[i].Point;
                    if (i == 0)
                    {
                        pathData += "M " + point.X + "," + point.Y + " "; 
                    }
                    else
                    {
                        pathData += "L " + point.X + "," + point.Y + " ";
                    }
                }
                pathData += "Z";
                this.polygon.Data = Geometry.Parse(pathData);
            }
        }

        private void DeletePolygon()
        {
            foreach (PolygonPoint point in this.points)
            {
                mainGrid.Children.Remove(point.Circle);
            }
        }

        public DrawPolygonControl()
        {
            InitializeComponent();
            mainGrid.Children.Add(this.polygon);
            this.polygon.MouseEnter += PolygonMouseEnter;
            this.polygon.MouseLeave += PolygonMouseLeave;
            this.polygon.MouseUp += GridMouseUp;
        }

        private void PolygonMouseLeave(object sender, MouseEventArgs e)
        {
            this.isRun = false;
        }

        private void PolygonMouseEnter(object sender, MouseEventArgs e)
        {
            this.isRun = true;
        }

        private void DrawClick(object sender, RoutedEventArgs e)
        {
            this.drawType = DrawType.Draw;
            DeletePolygon();
            this.polygon.Data = null;
        }

        private void EditClick(object sender, RoutedEventArgs e)
        {
            this.drawType = DrawType.Edit;
        }

        private double PPDistance(Point p1, Point p2)
        {
            return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
        }

        private EditType GetEditType(Point point)
        {
            double distance = 5;
            int length = this.points.Count;
            if (this.isRun)
            {
                this.selectIndex = -1;
                return EditType.Internal;
            }
            else
            {
                for (int i = 0; i < length; i++)
                {
                    Point select = this.points[i].Point;
                    if (PPDistance(point, select) < distance)
                    {
                        this.selectIndex = i;
                        return EditType.Point;
                    }
                }
                this.selectIndex = -1;
                return EditType.None;
            }
        }

        private void GridMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                this.downPoint = e.GetPosition(mainGrid);
                switch (this.drawType)
                {
                    case DrawType.Draw:
                        DeletePolygon();
                        if (this.pointIndex == -1)
                        {
                            this.points.Clear();
                            this.points.Add(new PolygonPoint { Point = new Point(this.downPoint.X, this.downPoint.Y) });
                            this.pointIndex+=2;
                        }
                        else
                        {
                            this.pointIndex++;
                        }
                        DrawPolygon();
                        break;
                    case DrawType.Edit:
                        this.editType = GetEditType(this.downPoint);
                        break;
                }
            }
            else if (e.RightButton == MouseButtonState.Pressed && this.drawType == DrawType.Draw)
            {
                DeletePolygon();
                DrawPolygon();
                DrawCircle();
                this.pointIndex = -1;
            }
        }

        private void GridMouseUp(object sender, MouseButtonEventArgs e)
        {
            this.editType = EditType.None;
            this.selectIndex = -1;
        }

        private void GridMouseMove(object sender, MouseEventArgs e)
        {
            switch (this.drawType)
            {
                case DrawType.Draw:
                    if (this.pointIndex != -1)
                    {
                        Point point = e.GetPosition(mainGrid);
                        if (this.pointIndex == this.points.Count)
                        {
                            this.points.Add(new PolygonPoint { Point = new Point(point.X, point.Y) });
                        }
                        else
                        {
                            this.points[this.pointIndex].Point = new Point(point.X, point.Y);
                        }
                        DeletePolygon();
                        DrawPolygon();
                    }
                    break;
                case DrawType.Edit:
                    Point curPoint = e.GetPosition(mainGrid);
                    switch (this.editType)
                    {
                        case EditType.Point:
                            this.points[this.selectIndex].Point = new Point(curPoint.X, curPoint.Y);
                            DeletePolygon();
                            DrawPolygon();
                            DrawCircle();
                            break;
                        case EditType.Internal:
                            double diffX = curPoint.X - this.downPoint.X;
                            double diffY = curPoint.Y - this.downPoint.Y;
                            foreach (PolygonPoint item in this.points)
                            {
                                item.Point = new Point(item.Point.X + diffX, item.Point.Y + diffY);
                            }
                            this.downPoint = curPoint;
                            DeletePolygon();
                            DrawPolygon();
                            DrawCircle();
                            break;
                        case EditType.None:
                        default:
                            break;
                    }
                    break;
            }
        }
    }
}